package com.cf.auth.config;

import com.cf.auth.granter.SwordTokenGranter;
import com.cf.common.constant.AuthConstant;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;

/**
 * 认证服务器
 *
 * @author ChenFeng
 * @date 2021/11/8 2:28 下午
 * @description
 */
@Order
@Configuration
@EnableAuthorizationServer
@AllArgsConstructor
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {

    private final TokenStore tokenStore;

    private final AuthenticationManager authenticationManager;

    private final UserDetailsService userDetailsService;

    private final DataSource dataSource;

    private final JwtAccessTokenConverter jwtAccessTokenConverter;

    private final RedisTemplate redisTemplate;

    private final TokenEnhancer jwtTokenEnhancer;

    /**
     * 配置端点
     * userDetailsService：登陆用户名密码及客户端id、客户端secret都是使用同一个处理器，目前由于两个密码加解密方式不一样，所以使用DelegatingPasswordEncoder(委派密码编码器)
     * tokenStore：自定义token，使用jwt
     * jwtAccessTokenConverter：生成jwt
     * @param endpoints
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        TokenGranter tokenGranter = SwordTokenGranter.getTokenGranter(authenticationManager, endpoints, redisTemplate);
        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService)
                .tokenStore(tokenStore)
                .tokenGranter(tokenGranter);

        //扩展token返回结果
        if (jwtAccessTokenConverter != null && jwtTokenEnhancer != null) {
            TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
            List<TokenEnhancer> enhancerList = new ArrayList<>();
            enhancerList.add(jwtTokenEnhancer);
            enhancerList.add(jwtAccessTokenConverter);
            tokenEnhancerChain.setTokenEnhancers(enhancerList);
            //jwt增强
            endpoints.tokenEnhancer(tokenEnhancerChain).accessTokenConverter(jwtAccessTokenConverter);
        }
    }

    /**
     * 配置客户端信息
     */
    @Override
    @SneakyThrows
    public void configure(ClientDetailsServiceConfigurer clients) {
        SwordClientDetailsService clientDetailsService = new SwordClientDetailsService(dataSource);
        clientDetailsService.setSelectClientDetailsSql(AuthConstant.BASE_CLIENT_STATEMENT + " where client_id = ?");
        clientDetailsService.setFindClientDetailsSql(AuthConstant.BASE_CLIENT_STATEMENT + " order by client_id");
        clients.withClientDetails(clientDetailsService);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
        oauthServer
                .allowFormAuthenticationForClients()
                .tokenKeyAccess("permitAll()") // 授权访问/oauth/token
                .checkTokenAccess("permitAll()"); // 授权访问/oauth/check_token
    }
}
